# $Id: models.py 7557b5d4d82b 2012/12/02 09:07:16 pgurumur $
# Copyright (c) 2009 Prabhu Gurumurthy  <pgurumur@gmail.com>
# 
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

## @package acl
# ACL model to have firewall ACL in the database and automatically
# generated based on the database entries

from django.db import models

from djanet.lib import exception, stdlib
from djanet.db import models as model
from djanet.db import netdb, choices

__version__ =  "$Revision: 7557b5d4d82b $"
__author__ = "$Author: pgurumur $"

## Network address object group
# Inherits the base db class
class AddressObjectGroup(model.BaseModel):
   name = models.CharField(unique = True,
         max_length = 24, help_text = "Object group name")
   version = models.CharField(max_length = 10, choices = choices.ADDR_FAMILY,
         help_text = "IP address family, either v4 or v6")
   address = model.IPAddressField(
         unique = True, help_text = "Address or network")
   prefix = models.IntegerField(help_text = "Prefix value")

   class Meta:
      db_table = u'network_objects'

   def __str__(self):
      return stdlib.my_str(self.name)

class AbsAccessList(model.BaseModel):
   acl_key = models.CharField(unique = True,
         max_length = 24, help_text = "ACL identification key",
         editable = False)
   acl_name = models.CharField(max_length = 24, help_text = "ACL name")
   device = models.ForeignKey(netdb.Device)
   version = models.CharField(max_length = 10, choices = choices.ADDR_FAMILY,
         help_text = "IP address family, either v4 or v6")
   sequence = models.IntegerField(help_text = "Sequence number of the ACL")
   interface = models.ForeignKey(netdb.Interface, null = True,
         blank = True)
   dest = model.IPAddressField(null = True, blank = True,
         help_text = "Destination address or network")
   dest_prefix = models.IntegerField(null = True, blank = True,
         help_text = "Destination prefix")
   src = model.IPAddressField(null = True, blank = True,
         help_text = "Source address or network")
   src_prefix = models.IntegerField(null = True, blank = True,
         help_text = "Source prefix")
   src_object = models.ForeignKey(AddressObjectGroup, null = True,
         unique = True, blank = True,
         related_name = "%(class)s_related_src")
   dst_object = models.ForeignKey(AddressObjectGroup, null = True,
         blank = True, unique = True,
         related_name = "%(class)s_related_dst")
   log = models.BooleanField(help_text = "Log ACL")
   action = models.CharField(max_length = 15, choices = choices.RULE_ACTIONS,
         help_text = "Action applied to the rule")
   direction = models.CharField(max_length = 10,
         choices = choices.ACL_DIRECTION, help_text = "ACL direction",
         null = True, blank = True)
   protocol = models.IntegerField(default = 0,
         help_text = "Protocol number for TCP, default: 6", editable = False)

   class Meta:
      abstract = True

   def __init__(self, *args, **kwargs):
      super(AbsAccessList, self).__init__(*args, **kwargs)

   def save(self, *args, **kwargs):
      self.acl_key = stdlib.GetRandomString(24)
      if self.protocol > 255:
         raise exception.ModelError, "protocol cannot be greater than 255"

      super(AbsAccessList, self).save(*args, **kwargs)

   @property
   def key(self):
      return self.acl_key

   def __str__(self):
      return stdlib.my_str(self.acl_name)

class PortObjectGroup(model.BaseModel):
   name = models.CharField(unique = True,
         max_length = 24, help_text = "Object group name")
   protocol = models.IntegerField(null = True, blank = True,
         help_text = "Protocol for the port")
   port = models.IntegerField(null = True, blank = True,
         help_text = "Port number")
   port_range = models.CommaSeparatedIntegerField(null = True, blank = True,
         max_length = 2, help_text = "Port range to use")

   class Meta:
      db_table = u'port_objects'

   def save(self, *args, **kwargs):
      if self.port > 65535:
         raise exception.ModelError, "port cannot be greater than 65535"

      if self.protocol > 255:
         raise exception.ModelError, "protocol cannot be greater than 255"

      super(PortObjectGroup, self).save(*args, **kwargs)

   def __str__(self):
      return stdlib.my_str(self.name)

class TCPAccessList(AbsAccessList):
   src_port = models.IntegerField(null = True, blank = True,
         help_text = "Source port number")
   dest_port = models.IntegerField(null = True, blank = True,
         help_text = "Destination port number")
   src_portobj = models.ForeignKey(PortObjectGroup, null = True,
         blank = True, related_name = "%(class)s_related_src")
   dest_portobj = models.ForeignKey(PortObjectGroup, null = True,
         blank = True, related_name = "%(class)s_related_dst")

   class Meta:
      db_table = u'tcp_acl'

   def __init__(self, *args, **kwargs):
      super(TCPAccessList, self).__init__(*args, **kwargs)
      self.protocol = 6

   def save(self, *args, **kwargs):
      if ((self.src_port > 65535) or (self.dest_port > 65535)):
         raise exception.ModelError, "invalid source or destination port"

      super(TCPAccessList, self).save(*args, **kwargs)

class UDPAccessList(AbsAccessList):
   src_port = models.IntegerField(null = True, blank = True,
         help_text = "Source port number")
   dest_port = models.IntegerField(null = True, blank = True,
         help_text = "Destination port number")
   src_portobj = models.ForeignKey(PortObjectGroup, null = True,
         blank = True, related_name = "%(class)s_related_src")
   dest_portobj = models.ForeignKey(PortObjectGroup, null = True,
         blank = True, related_name = "%(class)s_related_dst")

   class Meta:
      db_table = u'udp_acl'

   def __init__(self, *args, **kwargs):
      super(UDPAccessList, self).__init__(*args, **kwargs)
      self.protocol = 17

   def save(self, *args, **kwargs):
      if ((self.src_port > 65535) or (self.dest_port > 65535)):
         raise exception.ModelError, "invalid source or destination port"

      super(UDPAccessList, self).save(*args, **kwargs)

class IPAccessList(AbsAccessList):
   class Meta:
      db_table = u'ip_acl'

class ICMPAccessList(AbsAccessList):
   def __init__(self, *args, **kwargs):
      super(ICMPAccessList, self).__init__(*args, **kwargs)
      self.protocol = 1

   class Meta:
      db_table = u'icmp_acl'
